#pragma once

#include <iostream>
#include <pthread.h>
#include <queue>
#include "Log.hpp"
#include "Task.hpp"

#define NUM 5

pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER;

class ThreadPool
{
private:
    int num;
    bool quit;
    std::queue<Task> _que;
    pthread_mutex_t _mtx;
    pthread_cond_t _cond;

    static ThreadPool *instance;

private:
    ThreadPool()
        : num(NUM), quit(false)
    {
        pthread_mutex_init(&_mtx, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }

    ThreadPool(const ThreadPool &tp){};

public:
    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_cond);
    }

public:
    void Lock()
    {
        pthread_mutex_lock(&_mtx);
    }

    void UnLock()
    {
        pthread_mutex_unlock(&_mtx);
    }

    void Wait()
    {
        pthread_cond_wait(&_cond, &_mtx);
    }

    void WakeUp()
    {
        pthread_cond_signal(&_cond);
    }

    static ThreadPool *GetInstance()
    {
        // 静态函数无法访问成员变量，因此只能使用全局的锁
        if (instance == nullptr)
        {
            pthread_mutex_lock(&_mutex);
            if (instance == nullptr)
            {
                instance = new ThreadPool();
                instance->InitThreadPool();
            }
            pthread_mutex_unlock(&_mutex);
        }
        return instance;
    }

    bool InitThreadPool()
    {
        for (int i = 0; i < num; ++i)
        {
            pthread_t tid;
            int ret = pthread_create(&tid, nullptr, ThreadRoutine, (void *)this);
            if (ret != 0)
            {
                LOG(FATAL, "create thread pool error!");
                return false;
            }
        }
        LOG(INFO, "create thread pool success!");
        return true;
    }

    static void *ThreadRoutine(void *args)
    {
        ThreadPool *tp = (ThreadPool *)args; // this指针

        while (true)
        {
            Task task;
            tp->Lock();

            while (tp->IsQueueEmpty()) // 重复确认资源是否存在
            {
                tp->Wait();
            }

            tp->PopTask(task);
            tp->UnLock();
            task.ProcessOn();
        }
    }

    void PushTask(const Task &task)
    {
        Lock();
        _que.push(task);
        UnLock();
        WakeUp();
    }

    void PopTask(Task &task)
    {
        task = _que.front();
        _que.pop();
    }

    bool IsQueueEmpty()
    {
        return _que.empty();
    }
};

ThreadPool *ThreadPool::instance = nullptr;